type Pos= (Int, Int)
data Card= N | S | E | O deriving (Show)
data Tubo= Tub Pos Card Card | Ini Pos Card | Fin Pos Card deriving (Show)
type Final= Tubo
type Inicio= Tubo
type Jugada= Tubo
data Estrategia= AB | RAND | VEC deriving (Show)
data Jugador= Jug String Estrategia [Jugada] 
data Tablero= T [Jugada] [Pos] Inicio Final Jugador

-- Configuracion del juego 
confFil::Int
confFil= 8

confCol::Int
confCol=8

confIni::Tubo
confIni= (Ini (3,2) N)


confFin::Tubo
confFin= (Fin (8,7) E)
--confFin= (Fin (3,4) S)

--------------------------

generarTubos::Pos->[Tubo]
generarTubos (x,y)= (Tub (x,y) N S):(Tub (x,y) E O):(Tub (x,y) S O):(Tub (x,y) N O):(Tub (x,y) E S):(Tub (x,y) E N):[]

generarPos::Int->Int->[Pos]
generarPos fil col=[(x,y) | x<-[0..fil], y<-[0..col]]

sacarPos::Pos->[Pos]->[Pos]
sacarPos xy xys= filter (\vw-> vw/=xy) xys

--Recorre una tuberia y dice si esta completa
tuberiaCompleta::Inicio->Final->[Jugada]->Bool
tuberiaCompleta ini fin []= conectada ini fin
tuberiaCompleta ini fin xs= if (conectada ini fin) then True
				else
				case siguienteTubo ini xs of
					Nothing-> conectada ini fin
					Just value->tuberiaCompleta value fin (sacarTubo value xs)

--saca un tubo de una lista de tubos
sacarTubo::Tubo->[Tubo]->[Tubo]
sacarTubo (Ini xy _) vws= filter (\(Tub mn _ _)-> mn/=xy) vws
sacarTubo (Tub xy _ _) vws= filter (\(Tub mn _ _)-> mn/=xy) vws

-- Me dice si 2 tubos estan conectados
conectada::Tubo->Final->Bool
conectada (Ini xy card1) (Fin vw card)= if ((adjacentes xy vw) && (sentido card card1)) then True else False
conectada (Tub xy card1 card2) (Fin vw card)= if ((adjacentes xy vw) && ((sentido card card1) || (sentido card card2))) then True else False

sentido:: Card->Card->Bool
sentido N S= True
sentido S N= True
sentido E O= True
sentido O E= True
sentido _ _= False

adjacentes::Pos->Pos->Bool
adjacentes (x,y) (v,w)= if ((x==v && abs(y-w)==1) || (y==w && abs(x-v)==1)) then True else False

--Retorna el tubo q continua
siguienteTubo::Inicio->[Tubo]->Maybe Inicio
siguienteTubo (Ini xy card) tubos= case filter (\(Tub vw card1 card2)-> adjacentes xy vw) tubos of
					[]-> Nothing
					value-> dameConectado (Ini xy card) value


--Recibe una lista de tubos adjacentes
dameConectado::Inicio->[Tubo]-> Maybe Inicio
dameConectado (Ini xy card) []= Nothing
dameConectado (Ini xy card) ((Tub vw card1 card2):vws)= if (sentido card card1) then Just (Ini vw card2) 
							else if (sentido card card2) then Just (Ini vw card1) 
							else dameConectado (Ini xy card) vws


-- las posiciones se generan por unica vez cuando se crea el tablero
jugadasPosibles :: Tablero -> [Jugada]
jugadasPosibles (T vw pos ini fin _)= map (\x-> Tub x N S) pos


-- El juego se da por terminado cuando si se conecta la tuberia completa o no hay mas posiciones disponibles
juegoTerminado  :: Tablero -> Bool
juegoTerminado (T vw pos ini fin (Jug _ VEC []))= True
juegoTerminado (T vw pos ini fin _)= if (tuberiaCompleta ini fin vw) || pos==[] then True else False


-- El jugador resulta ganador cuando se completa la tuberia
jugadorGanador  :: Tablero -> Maybe Jugador
jugadorGanador (T vw pos ini fin jug)= if (tuberiaCompleta ini fin vw) then Just jug else Nothing


-- le llega un tablero y una jugada valida
aplicar:: Tablero -> Jugada -> Tablero
aplicar (T vw pos ini fin (Jug nom VEC xs)) (Tub xy card1 card2)= T ((Tub xy card1 card2):vw) (sacarPos xy pos) ini fin (Jug nom VEC (sacarTubo (Tub xy card1 card2) xs))
aplicar (T vw pos ini fin jug) (Tub xy card1 card2)= T ((Tub xy card1 card2):vw) (sacarPos xy pos) ini fin jug


elegirJugada :: Jugador -> [Jugada] -> Jugada
elegirJugada (Jug nombre estr (x:[])) _= x
elegirJugada (Jug nombre estr (x:xs)) _= case estr of
					--	AB ->
					--	RAND ->
						VEC -> x

proxJugada:: Tablero->Jugada
proxJugada (T vw pos ini fin jug)= elegirJugada jug (jugadasPosibles (T vw pos ini fin jug))


nombre:: Jugador -> String
nombre (Jug nombre _ _)= nombre

-- Arma el tablero segun la configuracion
tableroInicial :: [Jugador] -> Tablero
tableroInicial (x:[])= (T [] (generarPos confFil confCol) confIni confFin x)



jugar:: Tablero -> Maybe Jugador
jugar tablero= if (juegoTerminado  tablero) then jugadorGanador tablero 
		else jugar (aplicar tablero (proxJugada tablero))

resultado::Maybe Jugador->String
resultado x= case x of
		Nothing-> "El jugador perdio"
		Just value-> "El ganador es: " ++ (nombre value)


